home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 52
/
Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso
/
Aminet
/
misc
/
emu
/
Apex-src.lha
/
XPL.XPL
< prev
next >
Wrap
Text File
|
2001-09-30
|
63KB
|
2,274 lines
\XPL.XPL APR-24-87
\XPL0 compiler for the 68000.
\Copyright 1977-1987 P.J.R. Boyle
\
\To convert to integer only, look for "$$$"
\
\REVISION HISTORY:
\1981, Added floating point, Loren Blaney
\JAN-85, Modified to produce 68000 op codes, L.B.
\DEC-85, Modified to produce 68881 op codes, L.B. & R.O.
\FEB-86, Modified for 32-bit operation for DFM Engineering, L.B.
\AUG-OCT-86, Fixed miscellaneous bugs, modified for new INT.68K, and
\ added register variables.
\DEC-09-86, Fixed module stuff
\MAR-87, V5.7, 32-bit MUL & DIV, added shift and EOR ops. Fixed undetected
\ mixed-mode error (for & !), and fixed passing more than 5 arguments.
\APR-87, Fixed global register variables, .OBJ buffer, and spring cleaning.
\ Changed string termination convention. Fixed stack balancing bug in 'IF'
\ expressions, $80000000 bug, hex overflow detection, and negative def bug.
\ Added a number of optimizations.
\
\CONTENTS:
\ MAIN: Display title and initialize
\ ERROR: Display error message and optionally continue
\ GETCH: Get a character from the source device
\ RATOM: Read an atom from source device
\ HEX2OUT: Output a hex byte (in ASCII) to disk
\ GEN: Output 68000 code to disk
\ FIXUP: Set contents of specified location to the current PC
\ LOOKUP: Look up an identifier name in the symbol table
\ INSERT: Insert an identifier into the symbol table
\ GETCON: Get a constant
\ PROCAL: Procedure calls
\ BOOLEXP: Generate code for a boolean expression
\ FACTOR: Generate code for a factor
\ STRCON: Text string constant ("string")
\ ARRAYCON: Constant array
\ SPECFAC: Special factors ('ADDR', string)
\ IDFAC: Identifier factors
\ SHIFTEXP: Generate code for a shift (e.g: A<<B)
\ TERM: Generate code for a term (e.g: A*B)
\ ALGEXP: Algebraic expression (e.g: A+B)
\ LOGEXP: Logical expression (e.g: A=B)
\ BOOLTERM: Boolean term (e.g: A&B)
\ SSTATEMENT: (for 'QUIT'S in 'CASE' statements)
\ STATEMENT: Parse and generate code
\ ASSIGN: Assignment statements
\ CASER: Case statements
\ PROCEDURE: Parse and generate code
\ CODDEC: 'CODE' declaration
\ CONDEC: 'DEFINE' declaration
\ VARDEC: 'INT','REAL', and 'ADDR' declarations
\ RVARDEC: 'REG' 'INT' declarations
\ EXTDEC: 'EXTERNAL' procedure declaration
\ FPRDEC: Forward PROCEDURE DECLARATIONS
\ EPRDEC: External procedure declarations
\ PROCDEC: 'PROCEDURE' declarations
code ABS= 0 RESERVE= 3 SWAP= 4 CHIN= 7
CHOUT= 8 CRLF= 9 INTIN= 10 INTOUT= 11
TEXT= 12 OPENI= 13 OPENO= 14 CLOSE= 15
SWAPWD= 117;
code real FLOAT= 49, RLRES= 46;
ext CHIN3= $C00, CHOUT3= $C06;
def TV= 0, KB= 0; \Device numbers
def TAB= $09, EOF= $1A, BEL= $07, EOL= $0D, SPACE= $20; \ASCII chars
def INTTBL= $800, \Address of intrinsic jump table
MUL1= $B04, \Base address of 32-bit multiply routines
DIV1= $B28, \Base address of 32-bit divide routines
VEXIT= $40C, \Address of exit vector for 'EXIT' statement
SYMAX= 1000 \Size of the symbol table
RLMAX= 100 \Size of real-constant symbol table
INTSIZE= 4 \No. of bytes in an integer (other changes required)
RLSIZE= 8 \No. of bytes in a real number (must be even)
\WARNING: TRI, TRA, & TRX must agree in size
SIGCHAR= 8 \No. of significant chars in an IDENT
QUITMAX= 100; \Maximum no. of 'QUIT'S in a 'LOOP'
addr ERRBUF; \Listing buffer for error messages
reg int DSP; \Data register number (pseudo stack pointer)
reg int CHAR; \Current character. Most of the time it
\ contains the terminator of the current ATOM
reg int ERRPTR; \Pointer for ERRBUF
int OBJBUF \.OBJ buffer (FIFO), compresses .OBJ file 20%
OBJFILL \Fill index
OBJEMPTY; \Empty index
int FPSP \Floating point register number
PSTOP \Greatest register (+1) in pseudo stack
\ This also points to the base of register variables
PSTOP0 \PSTOP when first entering procedure (for HPI & RET)
FPSTOP \Greatest register (+1) in FP pseudo stack
\ Also points to the base of real register variables
ERRCNT \Error counter
DEFAULT \Default options array. WARNING! not rommable
LSTDEV \Listing output device number
\ CASEIN \\Boolean: upper/lower case for (')
ATOM \Present atom descriptor
\ Contains reserved word hash or the ASCII for a
\ special character. Contains 0 if the atom is a
\ constant or an identifier
ATYPE; \Present ATOM type descriptor
def \ATYPE\ SPECIAL, IDENTIFIER, INTCON, REALCON;
addr IDENT; \Array: current identifier name
int HASH \Current identifier hash code
IATOM; \Value of current integer constant
real RLATOM; \Real constant from procedure "RATOM"
int IDTYPE; \Present identifier type descriptor (order is critical)
def UNDEF= 0 \Undefined identifier
ADDRVAR= 1 \Address variable ID (IDTYPE = INTEGER)
INVAR= 3 \Integer variable ID (odd nos.= INTEGER)
RLVAR= 4 \Real variable ID
INCON= 5 \Integer constant ID
RLCON= 6 \Real constant ID
INPROC= 7 \Integer procedure ID
RLPROC= 8 \Real procedure ID
INEPROC= 9 \Integer external procedure ID
RLEPROC= 10 \Real external procedure ID
INFPROC= 11 \Integer forward procedure ID
RLFPROC= 12 \Real forward procedure ID
ININT= 15 \Integer intrinsic ID
RLINT= 16 \Real intrinsic ID
INEXT= 17 \Integer external procedure ID
RLEXT= 18; \Real external procedure ID
int LEV \Level (static) of current identifier
VAL \Value or address of current identifier
SYMNUM \Position in SYMTBL of current identifer
FACTYP; \Factor (or operand) type (INTEGER or REAL)
def \FACTYP\ INTEGER, REAL;
int FIXES \Array: 'QUIT' fixes still outstanding
PC \Program counter
LASTOP \Previous opcode used by GEN
LASTLEV \Previous level used by GEN
LASTVAL \Previous value used by GEN
LEVEL \Level (static) of current procedure
NOSYM \Current number of symbols in symbol table
FIXCNT \Count of the number of outstanding 'QUIT'S
STKLOD \No. of integers left on stack by 'FOR' or 'CASE'
NORLSY \Current number of real constants in table
II; \Scratch
addr HEXDIGIT; \Array of hex digits (0 - F)
\SYMBOL TABLE ARRAYS:
addr SYMBOL \Identifier name (IDENT)
SYMTYP; \Type descriptors (IDTYPE)
int SYMVAL; \Value or address (VAL)
addr SYMLEV; \Level (LEV)
int SYMPNT; \List linkage pointers
int BOXES; \Hash boxes (symbol list headers)
real RLTBL; \Real constant table
\RESERVED WORD HASHES:
def ADRSYM= 25797 BEGSYM= 26057 CASEYM= 25046 CODSYM= 28615
DEFSYM= 26058 DOSYM= 28516 ELSEYM= 27864 ENDSYM= 28361
EXITYM= 30926 EXTNYM= 30937 FALSYM= 25042 FFUNYM= 26331
FORSYM= 28632 FPRSYM= 28888 FUNSYM= 30164 GESYM= 25959
GETSYM= 26075 IFSYM= 26217 INTSYM= 28381 LESYM= 25964
LOOPYM= 28635 NOTSYM= 28642 OFSYM= 26223 PROCYM= 29407
QUITYM= 30170 REALYM= 26067 REPSYM= 26082 RETSYM= 26086
THENYM= 26841 TRUSYM= 29417 UNTSYM= 28393 WHILYM= 26848
EPRSYM= 28887 EFUNYM= 26330 LNKSYM= 27098 OTHSYM= 29911
REGSYM= 26073 LSLSYM= 29656, LSRSYM= 29662;
proc ERROR(ERRNO); \Send error message to the TV
int ERRNO;
int ERR, CH, I;
addr STRING;
def MAXERR= 68; \Maximum error number
begin
if LSTDEV # TV then
begin
TEXT(TV,"
. . . ");
for I:= 0,$FF do \Display last 256 characters
begin
CH:= ERRBUF(ERRPTR);
if CH#0 then CHOUT(TV,CH);
ERRPTR:= (ERRPTR+1) & $FF; \Bump circular pointer
end;
end;
ERR:= RESERVE((MAXERR+1) *INTSIZE);
for I:= 0, MAXERR do ERR(I):= "?"; \Unused error nos. = "?"
ERR(0):= "I'M VERY CONFUSED"; \Internal error
ERR(1):= "TOO MANY VARIABLES";
ERR(2):= "TOO MANY REAL CONSTANT NAMES";
ERR(3):= "TOO MANY NAMES";
ERR(4):= "TOO MANY 'QUITS'";
ERR(5):= "TOO MANY STATIC LEVELS";
ERR(6):= "NUMBER OUT OF RANGE";
ERR(7):= ERR(6); \For intrinsic declarations
ERR(8):= "TOO MANY REGISTER VARIABLES";
ERR(10):= "UNDECLARED NAME";
ERR(11):= "NAME ALREADY DECLARED";
ERR(20):= "ILLEGAL START OF A STATEMENT"; \In ASSIGN
ERR(21):= "^":=^"*";
ERR(22):= "'THEN'*";
ERR(23):= "'DO'*";
ERR(24):= "^",^"*";
ERR(26):= "ILLEGAL FACTOR"; \Unrecognizable special factor
ERR(27):= "STATEMENT STARTING WITH A CONSTANT"; \In ASSIGN
ERR(28):= "'UNTIL'*";
ERR(29):= "'OTHER'*";
ERR(30):= "'ELSE'*";
ERR(31):= "DIGIT*";
ERR(33):= "INTEGER VARIABLE*"; \In a 'FOR' statement
ERR(40):= "^"=^"*";
ERR(41):= "^";^"*";
ERR(42):= "CONSTANT*"; \In GETCON
ERR(43):= "VARIABLE*"; \For an 'ADDR' operator
ERR(44):= "^")^"*";
ERR(45):= "NAME*";
ERR(46):= "MIXED MODE";
ERR(47):= "INTEGER*";
ERR(48):= "'OF'*";
ERR(49):= "^":^"*";
ERR(50):= "^"]^"*";
ERR(52):= "STATEMENT STARTING WITH 'ELSE'";
ERR(53):= "STATEMENT STARTING WITH 'OTHER'";
ERR(55):= "VARIABLE DECLARATION*";
ERR(60):= "'QUIT' NOT IN A 'LOOP'";
ERR(61):= "EOF*";
ERR(62):= "EOF INSIDE A BLOCK";
ERR(63):= "EOF INSIDE A STRING";
ERR(65):= "'FPROC' & ITS 'PROC' NOT AT SAME LEVEL";
ERR(66):= "'FPROC' REFERENCE NOT FOUND";
ERR(67):= "'PROC' OR 'FUNC'*";
ERR(68):= "'EPROC'S AND 'LINK'S MUST BE GLOBAL";
CHOUT(TV,BEL);
TEXT(TV,"
***** ERROR NO. "); INTOUT(TV,ERRNO); TEXT(TV," *****
");
STRING:= ERR(ERRNO);
I:= 0;
loop [CH:= STRING(I); \Output message
if CH = 0 then quit;
if CH = ^* then TEXT(TV," EXPECTED BUT NOT FOUND")
else CHOUT(TV,CH);
I:= I +1];
CRLF(TV);
TEXT(TV,"ATTEMPT TO CONTINUE (Y/N)? ");
OPENI(KB);
if CHIN(KB) = ^N then [CLOSE(LSTDEV); exit];
ERRCNT:= ERRCNT +1;
end; \ERROR
proc LIST(CH); \Save the last 256 characters in case of an error
int CH;
begin
ERRBUF(ERRPTR):= CH;
ERRPTR:= (ERRPTR+1) & $FF;
if LSTDEV # 7 then CHOUT(LSTDEV,CH); \Fast filter if null device
end; \LIST
proc GETCH; \Get a character from the disk
\Filters out comments and does case shift.
\(This procedure is optimized for speed.)
begin
CHAR:= CHIN3;
ERRBUF(ERRPTR):= CHAR; \Save the last 256 characters in case of an error
ERRPTR:= (ERRPTR+1) & $FF;
if 7 # LSTDEV then CHOUT(LSTDEV,CHAR); \Fast filter if null device
while ^\ = CHAR do \Filter out comments
begin
loop begin
CHAR:= CHIN3;
ERRBUF(ERRPTR):= CHAR;
ERRPTR:= (ERRPTR+1) & $FF;
if 7 # LSTDEV then CHOUT(LSTDEV, CHAR);
case CHAR of
EOL: quit;
^\: quit;
EOF: return \Don't filter out EOF's
other;
end;
CHAR:= CHIN3; \Get first character after comment
ERRBUF(ERRPTR):= CHAR;
ERRPTR:= (ERRPTR+1) & $FF;
if 7 # LSTDEV then CHOUT(LSTDEV, CHAR);
end;
\The compiler runs 2.5% faster if the case shift is not used
\if CHAR = ^' then [CASEIN:= ~CASEIN; CHAR:= SPACE] \\Shift case
\else if CASEIN then \\Switch to lower case if CASEIN is true
\ [if CHAR>=^A & CHAR<=^Z then CHAR:= CHAR+32];
end; \GETCH
proc RATOM; \Read an atom
\Outputs: ATOM, ATYPE, IDENT, HASH, IATOM, CHAR, RLATOM.
\ (This procedure is optimized for speed.)
int LEN, I, NEG, EXP;
real FRACT, DENOM;
proc RFRACT; \Read the fractional part of a real no.
begin
ATYPE:= REALCON; ATOM:= 0;
GETCH;
FRACT:= FLOAT(0); DENOM:= FLOAT(10); \(10.0 is not as portable)
while CHAR>=^0 & CHAR<=^9 do
[FRACT:= FRACT +FLOAT(CHAR-^0) /DENOM;
DENOM:= DENOM *FLOAT(10);
GETCH];
RLATOM:= RLATOM +FRACT;
end; \RFRACT
proc REXP; \Read an exponent, if any
if CHAR=^E then
begin
ATYPE:= REALCON;
GETCH;
if CHAR=^- then [NEG:= true; GETCH]
else NEG:= false;
if CHAR=^+ then GETCH;
EXP:= 0;
if CHAR<^0 ! CHAR>^9 then ERROR(31);
while CHAR>=^0 & CHAR<=^9 do
[EXP:= EXP *10 +CHAR-^0; GETCH];
if NEG then EXP:= -EXP;
while EXP>0 do
[RLATOM:= RLATOM *FLOAT(10); EXP:= EXP-1];
while EXP<0 do
[RLATOM:= RLATOM /FLOAT(10); EXP:= EXP+1];
end; \REXP
begin \RATOM
while $20 >= CHAR \space\ do
begin \Skip spaces, tabs, returns, LF's, & FF's, etc.
\Don't go past EOF
if EOF = CHAR then [ATYPE:= SPECIAL; ATOM:= EOF; return];
GETCH;
end;
if ^a <= CHAR then if CHAR <= ^z then \RESERVED WORD
[ATYPE:= SPECIAL;
ATOM:= CHAR; GETCH;
ATOM:= ATOM +SWAP(CHAR); GETCH;
if CHAR>=^a & CHAR<=^z then [ATOM:= ATOM+CHAR; GETCH];
while ^a<=CHAR & CHAR<=^z do GETCH;
if ATOM=TRUSYM then
[ATYPE:= INTCON; ATOM:= 0; IATOM:= true; return];
if ATOM=FALSYM then
[ATYPE:= INTCON; ATOM:= 0; IATOM:= false];
return];
if ^A <= CHAR then if CHAR <= ^Z then \IDENTIFIER
begin
ATYPE:= IDENTIFIER; ATOM:= 0;
IDENT(0):= CHAR; HASH:= CHAR; GETCH;
LEN:= 1;
loop case of
^A<=CHAR & ^Z>=CHAR, CHAR>=^0 & CHAR<=^9, CHAR=^_ :
begin
if SIGCHAR > LEN then
[IDENT(LEN):= CHAR;
HASH:= HASH +CHAR;
LEN:= LEN +1];
GETCH;
end
other quit;
for LEN:= LEN, SIGCHAR-1 do
[IDENT(LEN):= SPACE; HASH:= HASH +SPACE];
HASH:= HASH & $FF;
return;
end;
if ^0 <= CHAR then if CHAR <= ^9 then \UNSIGNED INTEGER
begin
ATYPE:= INTCON; \Assume integer until shown otherwise
ATOM:= 0;
IATOM:= CHAR -^0; GETCH;
loop begin
I:= IATOM;
if CHAR<^0 ! CHAR>^9 then quit;
I:= IATOM *10 + CHAR-^0;
if I<0 \integer overflow\ then quit;
IATOM:= I;
GETCH;
end;
\Remove the following line for integer-only version of the compiler $$$
\ RLATOM:= FLOAT(IATOM); \\*** DEBUG ***
IATOM:= I; \(can't FLOAT($80000000))
while CHAR>=^0 & CHAR<=^9 do \More digits; must be real
[RLATOM:= RLATOM *FLOAT(10) + FLOAT(CHAR-^0);
GETCH];
if CHAR=^. then RFRACT; \UNSIGNED REAL
REXP;
if ATYPE=INTCON & IATOM<0 & IATOM#$80000000 then ERROR(6);
return;
end;
case CHAR of
^.: [RLATOM:= FLOAT(0); \UNSIGNED REAL
RFRACT;
REXP;
return];
^$: begin \UNSIGNED HEX INTEGER
ATYPE:= INTCON; ATOM:= 0;
GETCH;
case of
CHAR>=^0 & CHAR<=^9: IATOM:= CHAR-^0;
CHAR>=^A & CHAR<=^F: IATOM:= CHAR-$37
other [\DIGIT EXPECTED\ ERROR(31); return];
loop [GETCH;
case of
CHAR>=^0 & CHAR<=^9: I:= CHAR-^0;
CHAR>=^A & CHAR<=^F: I:= CHAR-$37
other return;
if IATOM & $F0000000 then ERROR(6);
IATOM:= IATOM <<4 + I];
end;
^^: [ATYPE:= INTCON; \META CHARACTER
ATOM:= 0;
CHAR:= CHIN3;
LIST(CHAR);
IATOM:= CHAR;
GETCH;
return];
^": [ATYPE:= SPECIAL; \SPECIAL CHARACTER
ATOM:= CHAR; \(' and backslash have no effect in strings)
CHAR:= CHIN3;
LIST(CHAR);
return]
other;
ATYPE:= SPECIAL; \SPECIAL CHARACTER
ATOM:= CHAR;
GETCH;
case CHAR of
^=: case ATOM of
^:: [GETCH; ATOM:= GETSYM];
^>: [GETCH; ATOM:= GESYM];
^<: [GETCH; ATOM:= LESYM]
other;
^<: [if ATOM = ^< then [GETCH; ATOM:= LSLSYM]];
^>: [if ATOM = ^> then [GETCH; ATOM:= LSRSYM]]
other;
end; \RATOM
proc SKIPIT; \Skip the rest of a statement for error recovery
begin
while ATOM#EOF & ATOM#^; & ATOM#ENDSYM & ATOM#^] &
ATOM#BEGSYM & ATOM#^[ do RATOM;
end; \SKIPIT
\----------------------------------------------------------------------
proc HEX2OUT(VAL); \Output a hex byte
reg int VAL;
begin
CHOUT3(HEXDIGIT(VAL>>4 & $0F));
CHOUT3(HEXDIGIT(VAL & $0F));
end; \HEX2OUT
proc HEX4OUT(VAL); \Output a 4-digit hex word to the disk
reg int VAL; \(Optimized for speed)
reg addr HD;
begin
HD:= HEXDIGIT;
CHOUT3(HD(VAL>>12 & $0F));
CHOUT3(HD(VAL>>8 & $0F));
CHOUT3(HD(VAL>>4 & $0F));
CHOUT3(HD(VAL & $0F));
end; \HEX4OUT
proc OPENOBJ; \Initialize OBJBUF pointers
begin
OBJFILL:= 0;
OBJEMPTY:= 0;
end; \OPENOBJ
proc CLOSEOBJ; \Dump remaining words in OBJBUF to disk
begin
while OBJEMPTY # OBJFILL do
[HEX4OUT(OBJBUF(OBJEMPTY));
OBJEMPTY:= (OBJEMPTY +1) &7];
end; \CLOSEOBJ
proc GENOP(OP); \Output the opcode value
int OP;
begin
OBJBUF(OBJFILL):= OP;
OBJFILL:= (OBJFILL +1) &7;
if OBJFILL = OBJEMPTY then
[HEX4OUT(OBJBUF(OBJEMPTY));
OBJEMPTY:= (OBJEMPTY +1) &7];
PC:= PC +2;
end; \GENOP
proc GENPC(DELTA); \Output new PC value, changed by DELTA
int DELTA; \Number of bytes, (must be negative, even value)
begin
PC:= PC +DELTA;
DELTA:= -DELTA >>1; \Convert -bytes to +words
if DELTA > ((OBJFILL -OBJEMPTY) &7) then ERROR(0);
OBJFILL:= (OBJFILL -DELTA) &7;
end; \GENPC
\======================================================================
proc GEN(OP, LEV, VAL, CL); \Generate opcode
int OP, \I2L-style opcode ($00..$3F)
LEV, \Static nesting level, if memory access required
VAL, \If memory access, offset from frame pointer
\ neg values are for register vars (-8 = D7, etc)
CL; \Distinguish relocated immediates from constants
int I, MASK, PTR;
addr A;
proc ONEOP(OP); \Output a one-register opcode
int OP;
begin
GENOP(OP + DSP-1);
end; \ONEOP
proc SRCOFF(OP); \Generate a source effective-address code
int OP; \ e.g: MOVE.L 4(A0),D1
begin
case of
VAL>0: [GENOP(OP + $28 + LEV); \off(Alev)
GENOP(VAL)];
VAL=0: GENOP(OP + $10 + LEV); \(Alev)
VAL<0: GENOP(OP -VAL -1) \Dn
other; \Register values are:
\REG: 0 1 2 3 4 5 6 7
end; \SRCOFF VAL: -1 -2 -3 -4 -5 -6 -7 -8
proc PUSHEM; \Push register p-stack
\This doesn't push all the registers, instead it has a one-register hysteresis
\ which allows any two adjacent values to simultaneously be in registers.
\ This does not affect condition codes. (See comments for BALANCE.)
int MASK, PTR, D;
begin
GENOP($48E7); \MOVEM.L D1-Dpstop-2,-(SP)
MASK:= 0; \Build register list mask
PTR:= $4000;
for D:= 1, PSTOP-2 do
[MASK:= MASK ! PTR; PTR:= PTR >>1];
GENOP(MASK);
GENOP($C141 + (PSTOP-1) <<9); \EXG Dpsotp-1,D1
DSP:= DSP -(PSTOP -2);
LASTOP:= -1; \Can't optimize
end; \PUSHEM
proc PULLEM; \Pull register p-stack
\(This does not affect condition codes.)
int MASK, PTR, D;
begin
GENOP($C141 + (PSTOP-1) <<9); \EXG Dpsotp-1,D1
GENOP($4CDF); \MOVEM.L (SP)+,D1-Dpstop-2
MASK:= 0; \Build register list mask
PTR:= 2;
for D:= 1, PSTOP-2 do
[MASK:= MASK!PTR; PTR:= PTR+PTR];
GENOP(MASK);
DSP:= DSP +PSTOP -2;
LASTOP:= -1; \Can't optimize
end; \PULLEM
proc NEED1;
\Need one p-stack value, Dsp. Make sure it is available in a register.
\ This does not affect condition codes.
begin
if DSP >= PSTOP then PUSHEM;
if DSP < 1 then PULLEM;
end; \NEED1
proc NEEDNOS;
\Need one p-stack value, Dsp-1 (NOS). Make sure it is available in a reg.
begin
DSP:= DSP -1;
\NEED1;
if DSP >= PSTOP then PUSHEM;
if DSP < 1 then PULLEM;
DSP:= DSP +1;
end; \NEEDNOS
proc NEED2;
\Need two p-stack values, Dsp and Dsp-1. Make sure they are in registers.
begin
if DSP >= PSTOP then PUSHEM;
if DSP <= 1 then PULLEM;
end; \NEED2
\----------------------------------------------------------------------
proc FPUSHEM; \Push floating-point register p-stack
\This doesn't push all the registers, instead it has a one-register hysteresis
\ Which allows any two adjacent values to simultaneously be in registers.
\ This does not affect condition codes.
begin
GENOP($F227); GENOP($7500); \FMOVE.D FP2,-(SP)
GENOP($F227); GENOP($7480); \FMOVE.D FP1,-(SP)
GENOP($F200); GENOP($0C80); \FMOVE.X FP3,FP1
FPSP:= FPSP -(FPSTOP -2);
LASTOP:= -1; \Can't optimize
end; \FPUSHEM
proc FPULLEM; \Pull floating-point register p-stack
\ This does not affect condition codes.
begin
GENOP($F200); GENOP($0580); \FMOVE.X FP1,FP3
GENOP($F21F); GENOP($5480); \FMOVE.D (SP)+,FP1
GENOP($F21F); GENOP($5500); \FMOVE.D (SP)+,FP2
FPSP:= FPSP +FPSTOP -2;
LASTOP:= -1; \Can't optimize
end; \FPULLEM
proc FNEED1;
\Need one p-stack value, FPsp. Make sure it is available in a register.
\ This does not affect condition codes.
begin
if FPSP >= FPSTOP then FPUSHEM;
if FPSP < 1 then FPULLEM;
end; \FNEED1
proc FNEEDNOS;
\Need one p-stack value, FPsp-1 (NOS). Make sure it is available in a reg.
begin
FPSP:= FPSP -1;
FNEED1;
FPSP:= FPSP +1;
end; \FNEEDNOS
proc FNEED2;
\Need two p-stack values, FPsp and FPsp-1. Make sure they are in registers.
begin
if FPSP >= FPSTOP then FPUSHEM;
if FPSP <= 1 then FPULLEM;
end; \FNEED2
proc BALANCE; \Balance the stack by setting it to a known state.
\This is required because the pseudo stack has hysteresis. Whenever two
\ Paths of the code converge, the stack must be in the same state for both.
\ This cannot, and does not, affect condition codes because of the 'FOR'
\ and 'CAJ' pseudo ops.
begin
if DSP = PSTOP-1 then PUSHEM else NEED1;
if FPSP = FPSTOP-1 then FPUSHEM else FNEED1;
end; \BALANCE
\----------------------------------------------------------------------
proc GENARG; \Generate code to pop proc arguments.
\That is, move them from the p-stack, and stack, to the heap.
int ARG, \Number of arguments remaining
NOS, \Next on stack
N, \Number of arguments currently in registers
MASK, \Register list for MOVEM instruction
PTR, \Bit pointer
I;
begin
ARG:= VAL /INTSIZE; \The total number of arguments
while ARG > 0 do
begin
NEEDNOS;
NOS:= DSP-1;
N:= if ARG<NOS then ARG else NOS; \Take smaller
if N >= 2 then
begin \Use MOVEM.L
MASK:= 0; \Build register list mask
PTR:= 2; \Initialize bit pointer to D1
\Move PTR to first register if it't not D1
for I:= 1, NOS-N do PTR:= PTR +PTR;
\Move PTR to last register, setting mask bits along the way.
for I:= 1, N do
[MASK:= MASK !PTR; PTR:= PTR +PTR];
ARG:= ARG -N; \Decrement argument counter
if ARG > 0 then
[GENOP($48ED); \MOVEM.L Dsp-1...Dsp-rem-1,4*arg(A5)
GENOP(MASK);
GENOP(INTSIZE *ARG)]
else [GENOP($48D5); \MOVEM.L Dsp-1...Dsp-rem-1,(A5)
GENOP(MASK)];
DSP:= DSP -N; \Pop registers (fixed up by NEEDNOS)
end
else begin \Only one argument in a register
ARG:= ARG -1;
if ARG > 0 then
[ONEOP($2B40); \MOVE.L Dsp-1,4*arg(A5)
GENOP(INTSIZE *ARG)]
else ONEOP($2A80); \MOVE.L Dsp-1,(A5)
DSP:= DSP -1; \Pop register
end;
end;
end; \GENARG
\----------------------------------------------------------------------
proc OPTIMIZE; int OP;
\Optimize by combining OP with a previous LOD or IMM.
\ I.e. try to replace the source register with the source of a previous LOD.
begin
case LASTOP of
$01: begin \LOD
GENPC(if LASTVAL>0 then -4 else -2);
OP:= OP & $FFC0; \Remove old source code bits
case of
LASTVAL>0: [GENOP(OP + $28 + LASTLEV); \off(Alev)
GENOP(LASTVAL)];
LASTVAL=0: GENOP(OP + $10 + LASTLEV); \(Alev)
LASTVAL<0: GENOP(OP -LASTVAL -1) \Dn
other;
end;
$0B: begin \IMM
GENPC(-6);
GENOP((OP & $FFC0) + $3C); \#xxx,Dsp-1
GENOP(SWAPWD(LASTVAL));
GENOP(LASTVAL);
end
other GENOP(OP); \Dsp,Dsp-1
end; \OPTIMIZE
proc OPTIMX(OP); \Optimize a 2-register opcode
int OP;
begin
DSP:= DSP-1;
NEED2;
OPTIMIZE(OP + (DSP-1) <<9 + DSP);
end; \OPTIMX
proc STOOFF; int OP;
\Generate a destination effective address code for STO opcode
begin \ E.g: MOVE.L D1,4(A0)
case of
VAL>0: [OPTIMIZE(OP + LEV <<9 + $140); \off(Alev)
GENOP(VAL)];
VAL=0: OPTIMIZE(OP + LEV <<9 + $80); \(Alev)
VAL<0: OPTIMIZE(OP + (-VAL-1) <<9) \Dn
other;
end; \STOOFF
proc GENCMP(OP); \Generate compare instruction
int OP;
begin
DSP:= DSP-1;
NEED2;
if LASTOP=\IMS\$24 & LASTVAL=0 then
[GENPC(-2);
ONEOP($4A80)] \TST.L Dsp-1
else OPTIMIZE($B080 + (DSP-1) <<9 + DSP); \CMP.L Dsp,Dsp-1
ONEOP(OP); \SCC Dsp-1
ONEOP($4880); \EXT.W Dsp-1
ONEOP($48C0); \EXT.L Dsp-1
end; \GENCMP
\----------------------------------------------------------------------
proc GENFCMP(OP); \Generate floating compare instruction
int OP;
begin
FPSP:= FPSP-1; \Compare NOS to TOS
FNEED2; \Compare dest to source
\Written: source, dest
GENOP($F200); \FCMP.S FPsp,FPsp-1
GENOP($0038 + FPSP <<10 + (FPSP-1) <<7);
\??? INCOMPATABLE WITH 68881 ???
\GENOP($F240 + DSP); \\FSCC Dsp
\GENOP(OP);
GENOP(OP +DSP); \SCC Dsp
GENOP($4880 + DSP); \EXT.W Dsp
GENOP($48C0 + DSP); \EXT.L Dsp
DSP:= DSP+1;
FPSP:= FPSP-1;
end; \GENFCMP
\----------------------------------------------------------------------
proc BRANCH(OP); \Generate branch instruction
int OP;
int I;
\Note that "fixed" locations are assumed to be generated as 0 or
\ as a jump-to-self initially. The short branch should not be used
\ in this case.
begin
I:= VAL - (PC+2);
if ABS(I)<=$7F & VAL#0 \forward ref\ & I#0 & VAL#PC then
GENOP(OP + (I&$FF))
else [GENOP(OP);
GENOP(VAL-PC)]; \Beware of +/- 32K address limitation
end; \BRANCH
proc GENBRA(OP);
int OP;
\Generate an optimized branch on condition to replace a conditional
\ opcode followed by a JOC opcode. (Saves 4 words.)
begin
GENPC(-6);
BRANCH(OP);
end; \GENBRA
begin \GEN
\NOTE: The p-stack grows upward starting with D1. DSP is the top of the
\ p-stack, an empty location. The convention is: PUSH = MOVE D0,(DSP)+
\ and PULL = MOVE -(DSP),D0
case OP of
$00:\EXIT\ [GENOP($4EF8); \JMP VEXIT.W
GENOP(VEXIT)]; \Warning: registers are not restored
$01:\LOD\ [NEED1;
SRCOFF($2000 + DSP <<9); \MOVE.L off(Alev),Dsp
DSP:= DSP+1];
$02:\LDX\ [NEEDNOS; \Indexed load (push) a byte LDX, LEV, OFF
if LASTOP=\IMS\$24 then
[GENPC(-2);
SRCOFF($2C40); \MOVEA.L off(Alev),A6
GENOP($7000 + (DSP-1) <<9); \MOVEQ #0,Dsp-1
if LASTVAL # 0 then
[GENOP($102E + (DSP-1)<<9); \MOVE.B lv(A6),Dsp-1
GENOP(LASTVAL);
OP:= -1] \(Can't optimize with JOC)
else GENOP($1016 + (DSP-1)<<9)] \MOVE.B (A6),Dsp-1
else
[OPTIMIZE($2C40 + DSP-1); \MOVEA.L Dsp-1,A6
\ [ONEOP($2C40); \\MOVEA.L Dsp-1,A6
SRCOFF($DDC0); \ADDA.L off(Alev),A6
GENOP($7000 + (DSP-1) <<9); \MOVEQ #0,Dsp-1
GENOP($1016 + (DSP-1) <<9)]; \MOVE.B (A6),Dsp-1
end;
$03:\STO\ [DSP:= DSP-1;
NEED1; \Store (pop) into a variable STO, LEV, OFF
if LASTOP=\IMS\$24 & VAL<0 then
[GENPC(-2); \MOVEQ #lastval,Dval
GENOP($7000 + (-VAL-1) <<9 + (LASTVAL & $FF))]
else if LASTOP=\IMS\$24 & LASTVAL=0 then
[GENPC(-2);
SRCOFF($4280)] \CLR.L off(Alev)
else STOOFF($2000 + DSP)]; \MOVE.L Dsp,off(Alev)
$04:\STX\ [DSP:= DSP-1;
NEED2; \Indexed store (pop) to a byte STX, LEV, OFF
SRCOFF($2C40); \MOVEA.L off(Alev),A6
GENOP($1D80 + DSP); \MOVE.B Dsp,0(A6,Dsp-1)
GENOP($0800 + (DSP-1) <<12);
DSP:= DSP-1]; \(POP BOTH)
$05:\CAL\ BRANCH($6100); \BSR ADDR
$06:\RET\ if LASTOP # $06 \RET\ then
[GENOP($2A48 + LEV); \MOVEA.L Alev,A5
\$$$
\ GENOP($F21F); GENOP($5480); \\FMOVE.D (SP)+,FP1
\ GENOP($F21F); GENOP($5500); \\FMOVE.D (SP)+,FP2
\ GENOP($F21F); GENOP($5580); \\FMOVE.D (SP)+,FP3
GENOP($4CDF); \MOVEM.L (SP)+,D1-Dsptop0-1/Alev
MASK:= $100 <<LEV; \Set mask for Alev
PTR:= 2; \Point to D1
for I:= 1, PSTOP0-1 do \Build register list mask
[MASK:= MASK !PTR; PTR:= PTR +PTR];
GENOP(MASK);
GENOP($4E75)]; \RTS
$07:\JMP\ begin
BALANCE;
if CL=64 then \Module linkage
[GENOP($4EF9); \JMP val.L
CLOSEOBJ;
CHOUT3(^#);
GENOP(SWAPWD(VAL));
GENOP(VAL)]
else BRANCH($6000); \BRA ADDR
end;
$08:\JOC\ begin \Jump on condition (false) JOC, ADDR
DSP:= DSP-1;
BALANCE; \(If the stack is changed, LASTOP:= -1)
case LASTOP of
$12:\EQ\ GENBRA($6600); \BNE ADDR
$13:\NE\ GENBRA($6700); \BEQ ADDR
$14:\GE\ GENBRA($6D00); \BLT ADDR
$15:\GT\ GENBRA($6F00); \BLE ADDR
$16:\LE\ GENBRA($6E00); \BGT ADDR
$17:\LT\ GENBRA($6C00); \BGE ADDR
$01,\LOD\ $0B,\IMM\
$0D,\ADD\ $0E,\SUB\ $11,\NEG\
$1A,\OR\ $1B,\AND\ $1C,\NOT\
$1D,\EOR\ $24,\IMS\ $3E,\LSL\
$3F:\LSR\ BRANCH($6700); \BEQ ADDR
$02:\LDX\ [GENPC(-4);
GENOP($4A16); \TST.B (A6)
BRANCH($6700)] \BEQ ADDR
other [NEED1;
GENOP($4A80 + DSP); \TST.L Dsp
BRANCH($6700)]; \BEQ ADDR
end;
$09:\HPI\ [GENOP($48E7); \MOVEM.L D1-Dsptop0-1/Alev,-(SP)
MASK:= $80 >>LEV; \Set mask for Alev
PTR:= $4000; \Point to D1
for I:= 1, PSTOP0-1 do \Build register list mask
[MASK:= MASK !PTR; PTR:= PTR >>1];
GENOP(MASK);
\$$$
\ GENOP($F227); GENOP($7580); \\FMOVE.D FP3,-(SP)
\ GENOP($F227); GENOP($7500); \\FMOVE.D FP2,-(SP)
\ GENOP($F227); GENOP($7480); \\FMOVE.D FP1,-(SP)
GENOP($204D + LEV <<9); \MOVEA.L A5,Alev
if VAL > 0 then
[GENOP($4BED); \LEA bytes(A5),A5
GENOP(VAL)]; \(Fast increment A5)
];
$0A:\ARG\ if LASTOP=\IMS\$24 & LASTVAL=0 & VAL=INTSIZE then
[GENPC(-2); \(Passing a zero)
GENOP($4295); \CLR.L (A5)
DSP:= DSP-1]
else if VAL=INTSIZE then \(Passing only one argument)
[OPTIMIZE($2A80 + DSP-1); \MOVE.L Dsp-1,(A5)
DSP:= DSP-1]
else GENARG;
$0B:\IMM\ begin
NEED1; \Immediate load of a value IMM, #XXX
if CL=7 then \Position-independant relocate for strings, etc.
[GENOP($4DFA); \LEA d(PC),A6
GENOP(VAL-PC);
GENOP($200E + DSP <<9); \MOVE.L A6,Dsp
OP:= -1] \Can't optimize
else [GENOP($203C + DSP <<9); \MOVE.L #xxx,Dsp
GENOP(SWAPWD(VAL)); \Gen high and low words
GENOP(VAL)];
DSP:= DSP+1;
end;
$0C:\CML\ [GENOP($4EB8); \JSR ADDR.W
GENOP(INTTBL + VAL *6)];
$0D:\ADD\ [DSP:= DSP-1;
NEED2;
if LASTOP=\IMS\$24 & LASTVAL<=8 & LASTVAL>=1 then
[GENPC(-2);
GENOP($5080 + (LASTVAL&$7) <<9 + DSP-1)] \ADDQ.L #val,Dsp-1
else OPTIMIZE($D080 + (DSP-1) <<9 + DSP)]; \ADD.L Dsp,Dsp-1
$0E:\SUB\ \TOS := NOS - TOS Dsp-1 := Dsp-1 - Dsp
[DSP:= DSP-1;
NEED2;
if LASTOP=\IMS\$24 & LASTVAL<=8 & LASTVAL>=1 then
[GENPC(-2);
GENOP($5180 + (LASTVAL&$7) <<9 + DSP-1)] \SUBQ.L #val,Dsp-1
else OPTIMIZE($9080 + (DSP-1) <<9 + DSP)]; \SUB.L Dsp,Dsp-1
$0F:\MUL\ [DSP:= DSP-1; \MULS Dsp,Dsp-1
NEED2;
GENOP($4EB8); GENOP(MUL1 + 6 *(DSP-2))]; \JSR MULx.W
$10:\DIV\ [DSP:= DSP-1;
NEED2; \TOS := NOS / TOS Dsp-1 := Dsp-1 / Dsp
GENOP($4EB8); GENOP(DIV1 + 6 *(DSP-2))]; \JSR DIVx.W
$11:\NEG\ [NEEDNOS; ONEOP($4480)]; \NEG.L Dsp-1
$12:\EQ\ GENCMP($57C0); \SEQ
$13:\NE\ GENCMP($56C0); \SNE
$14:\GE\ GENCMP($5CC0); \SGE
$15:\GT\ GENCMP($5EC0); \SGT
$16:\LE\ GENCMP($5FC0); \SLE
$17:\LT\ GENCMP($5DC0); \SLT
$18:\FOR\ [OPTIMX($B080); \CMP.L Dsp,Dsp-1
BALANCE; \(Does not affect condition codes)
BRANCH($6C00); \BGE ADDR
DSP:= DSP-1]; \(Overall effect of popping)
$19:\INC\ \Increment and push INC, LEV, OFF
SRCOFF($5280); \ADDQ.L #1,off(Alev)
$1A:\OR\ OPTIMX($8080); \OR.L Dsp,Dsp-1
$1B:\AND\ OPTIMX($C080); \AND.L Dsp,Dsp-1
$1C:\NOT\ [NEEDNOS; ONEOP($4680)]; \NOT.L Dsp-1
$1D:\EOR\ [DSP:= DSP-1; \(Can't optimize)
NEED2;
GENOP($B180 + DSP <<9 + DSP-1)]; \EOR.L Dsp,Dsp-1
$1E:\DBA\ begin \TOS:= NOS + TOS*4 (DBA)
DSP:= DSP-1; \ Dsp=TOS=INDEX, Dsp-1 = NOS = base address
NEED2;
if LASTOP=\IMS\$24 & LASTVAL<32 then
begin
GENPC(-2);
case LASTVAL of
0: ; \(save 3 wds, 24 cys)
1: ONEOP($5880); \ADDQ.L #4,Dsp-1
2: ONEOP($5080) \ADDQ.L #8,Dsp-1
other [GENOP($7000 + DSP<<9 + LASTVAL<<2); \MOVEQ #lastval*4,Dsp
GENOP($D080 + (DSP-1) <<9 + DSP)]; \ADD.L Dsp,Dsp-1
end
else
[GENOP($E580 + DSP); \ASL.L #2,Dsp
GENOP($D080 + (DSP-1) <<9 + DSP)]; \ADD.L Dsp,Dsp-1
end;
$1F:\STD\ [DSP:= DSP-1;
NEED2; \Indirect save (STD) (NOS) := TOS
ONEOP($2C40); \MOVEA.L Dsp-1,A6
GENOP($2C80 + DSP); \MOVE.L Dsp,(A6)
DSP:= DSP-1]; \(Pop both)
$20:\DBX\ begin
DSP:= DSP-1;
NEED2; \Indirect get (DBX) TOS := (TOS*4 + NOS)
if LASTOP=\IMS\$24 then
[GENPC(-2);
ONEOP($2C40); \MOVEA.L Dsp-1,A6
if LASTVAL # 0 then
[GENOP($202E + (DSP-1)<<9); \MOVE.L lv*4(A6),Dsp-1
GENOP(LASTVAL<<2)]
else GENOP($2016 + (DSP-1)<<9)] \MOVE.L (A6),Dsp-1
else
[ONEOP($2C40); \MOVEA.L Dsp-1,A6
GENOP($E580 + DSP); \ASL.L #2,Dsp
GENOP($2036 + (DSP-1) <<9); \MOVE.L 0(A6,Dsp),Dsp-1
GENOP($0800 + DSP <<12)];
end;
$21:\ADR\ [NEED1; \Load (push) address of variable ADDR, LEV, OFF
SRCOFF($4DC0); \LEA off(Alev),A6
GENOP($200E + DSP <<9); \MOVE.L A6,Dsp
DSP:= DSP+1];
$24:\IMS\ [NEED1; \Short immediate
GENOP($7000 + DSP <<9 + (VAL&$FF)); \MOVEQ #val,Dsp
DSP:= DSP+1];
$25:\CAJ\ [OPTIMX($B080); \CMP.L Dsp,Dsp-1
BALANCE; \(Does not affect condition codes)
BRANCH($6600)]; \BNE ADDR
$27:\BAL\ BALANCE;
$28:\DRP\ [NEED1; \(Usually doesn't generate any code)
DSP:= DSP-1];
$29:\EXT\ [GENOP($4EB9); \JSR ADDR.L
GENOP(SWAPWD(VAL));
GENOP(VAL)];
$2A:\FLOD\ begin
FNEED1;
if VAL<0 then
[GENOP($F200); \FMOVE.X FP0,FPsp
GENOP(FPSP <<7)]
else [GENOP($F228 + LEV); \FMOVE.D off(Alev),FPsp
GENOP($5400 + FPSP <<7);
GENOP(VAL)];
FPSP:= FPSP+1;
end;
$2B:\FSTO\ begin
FPSP:= FPSP-1;
FNEED1;
if VAL<0 then
[GENOP($F200); \FMOVE.X FPsp,FP0
GENOP(FPSP <<10)]
else [\NEED1;
GENOP($F228 + LEV); \FMOVE.D FPsp,off(Alev)
GENOP($7400 + FPSP <<7);
GENOP(VAL)];
end;
$2C:\FIMM\ begin
FNEED1; \Immediate load of a value FIMM, #XXX
if CL=7 then \Position-independant relocate for strings, etc.
[GENOP($4DFA); \LEA d(PC),A6
GENOP(VAL-PC);
GENOP($200E); \MOVE.L A6,D0
GENOP($F200); \FMOVE.L D0,FP0
GENOP($4000);
GENOP($F200); \FMOVE.X FP0,FPsp
GENOP(FPSP <<7)]
else [GENOP($F23C); \FMOVE.D #xxx,FPsp
GENOP($5400 + FPSP <<7);
A:= addr RLATOM; \To access individual bytes in RLATOM
CLOSEOBJ;
for I:= 0,RLSIZE-1 do HEX2OUT(A(I));
PC:= PC +RLSIZE];
FPSP:= FPSP+1;
end;
$2D:\FADD\ [FPSP:= FPSP-1; \FADD.X FPsp,FPsp-1
FNEED2;
GENOP($F200);
GENOP($0022 + FPSP <<10 + (FPSP-1) <<7)];
$2E:\FSUB\ [FPSP:= FPSP-1; \FSUB.X FPsp,FPsp-1
FNEED2;
GENOP($F200); \TOS := NOS - TOS FPsp-1 := FPsp-1 - FPsp
GENOP($0028 + FPSP <<10 + (FPSP-1) <<7)];
$2F:\FMUL\ [FPSP:= FPSP-1; \FMUL.X FPsp,FPsp-1
FNEED2;
GENOP($F200); \TOS := NOS / TOS FPsp-1 := FPsp-1 * FPsp
GENOP($0023 + FPSP <<10 + (FPSP-1) <<7)];
$30:\FDIV\ [FPSP:= FPSP-1; \FDIV.X FPsp,FPsp-1
FNEED2;
GENOP($F200); \TOS := NOS / TOS FPsp-1 := FPsp-1 / FPsp
GENOP($0020 + FPSP <<10 + (FPSP-1) <<7)];
$31:\FNEG\ [FNEEDNOS;
GENOP($F200); \FNEG.X FPsp-1
GENOP($001A + (FPSP-1) <<10 + (FPSP-1) <<7)];
\Compare NOS to TOS (NOS - TOS)
$32:\FEQ\ GENFCMP(\$0001\$57C0); \??? INCOMPATABLE WITH 68881 ???
$33:\FNE\ GENFCMP(\$000E\$56C0);
$34:\FGE\ GENFCMP(\$0013\$5CC0);
$35:\FGT\ GENFCMP(\$0012\$5EC0);
$36:\FLE\ GENFCMP(\$001D\$5FC0); \(not GT)
$37:\FLT\ GENFCMP(\$0014\$5DC0);
$38:\TRA\ [FPSP:= FPSP-1; \TOS:= TOS *8 + NOS
FNEED1; \Dsp-1 Dsp-1 FPsp-1
NEEDNOS;
GENOP($E780 + DSP-1); \ASL.L #3,Dsp-1
GENOP($F200); \FMOVE.X FPsp,FP0
GENOP(FPSP <<10);
GENOP($F200); GENOP($6000); \FMOVE.L FP0,D0
GENOP($D080 + (DSP-1) <<9)]; \ADD.L D0,Dsp-1
$39,\TRX\
$3A:\TRI\ [DSP:= DSP-1; \TOS:= (TOS *8 + NOS)
NEED1; \FPsp-1 Dsp-1 FPsp-1
FNEEDNOS;
GENOP($E780 + DSP); \ASL.L #3,Dsp
GENOP($F200); \FMOVE.X FPsp-1,FP0
GENOP((FPSP-1) <<10);
GENOP($F200); GENOP($6000); \FMOVE.L FP0,D0
GENOP($D080 + DSP); \ADD.L Dsp,D0
GENOP($2C40); \MOVEA.L D0,A6
GENOP($F216); \FMOVE.D (A6),FPsp-1
GENOP($5400 + (FPSP-1) <<7)];
$3B:\STT\ [DSP:= DSP-1; \Store TOS at NOS, pop both
NEED1; \ FPsp-1 Dsp-1
FPSP:= FPSP-1;
FNEED1;
GENOP($2C40 + DSP); \MOVEA.L Dsp,A6
GENOP($F216); \FMOVE.D FPsp,(A6)
GENOP($7400 + FPSP <<7)];
$3E:\LSL\ [DSP:= DSP-1; \TOS:= NOS << TOS
NEED2; \Dsp-1:= Dsp-1 << Dsp
if LASTOP=\IMS\$24 & LASTVAL<=8 & LASTVAL>=1 then
[GENPC(-2);
GENOP($E188 + (LASTVAL&$7) <<9 + DSP-1)] \LSL.L #val,Dsp-1
else GENOP($E1A8 + DSP <<9 + DSP-1)]; \LSL.L Dsp,Dsp-1
$3F:\LSR\ [DSP:= DSP-1; \TOS:= NOS >> TOS
NEED2; \Dsp-1:= Dsp-1 >> Dsp
if LASTOP=\IMS\$24 & LASTVAL<=8 & LASTVAL>=1 then
[GENPC(-2);
GENOP($E088 + (LASTVAL&$7) <<9 + DSP-1)] \LSR.L #val,Dsp-1
else GENOP($E0A8 + DSP <<9 + DSP-1)] \LSR.L Dsp,Dsp-1
other ERROR(0);
if CL&32 then \Output dummy bytes for constant real array pointers
begin
CLOSEOBJ;
for I:= INTSIZE, RLSIZE-1 do [HEX2OUT(0); PC:= PC+1];
end;
LASTOP:= OP;
LASTLEV:= LEV;
LASTVAL:= VAL;
end; \GEN
\======================================================================
proc FIXUP(I); \Fix forward references
int I;
begin
GEN(\BAL\$27, 0, 0, 0); \First, balance the stack
CLOSEOBJ;
CHOUT3(^^); HEX4OUT(I+2); \Skip opcode
LASTOP:= -1; \Don't optimize
end; \FIXUP
proc LOOKUP; \Lookup identifier in symbol table
\Inputs: IDENT, HASH
\Outputs: IDTYPE, VAL, LEV, SYMNUM.
\If two identifiers of the same name are in the symbol table
\ then the most recent entry is used.
int I, K, PNTR;
begin
PNTR:= BOXES(HASH);
loop begin
if PNTR = \empty\ -1 then [IDTYPE:= UNDEF; quit];
I:= 0; K:= PNTR;
while IDENT(I)=SYMBOL(K) & I<SIGCHAR do
[I:= I +1; K:= K +SYMAX];
if I=SIGCHAR then \FOUND
[IDTYPE:= SYMTYP(PNTR);
VAL:= SYMVAL(PNTR);
LEV:= SYMLEV(PNTR);
SYMNUM:= PNTR; \(FOR FORWARD PROC)
quit];
PNTR:= SYMPNT(PNTR);
end;
end; \LOOKUP
proc INSERT(STYP, SLEV, SVAL);
\Insert the current identifier into the symbol table
\Inputs: STYP, SLEV, SVAL, IDENT, HASH, NOSYM, SYMBOL, & BOXES.
int STYP, SLEV, SVAL;
int I, K;
begin
LOOKUP;
if IDTYPE#UNDEF then if LEV = LEVEL then \collision\ ERROR(11);
if NOSYM >= SYMAX then \table full\ [ERROR(3); NOSYM:= SYMAX -1];
K:= NOSYM;
for I:= 0, SIGCHAR-1 do [SYMBOL(K):= IDENT(I); K:= K +SYMAX];
SYMTYP(NOSYM):= STYP;
SYMLEV(NOSYM):= SLEV;
SYMVAL(NOSYM):= SVAL;
SYMPNT(NOSYM):= BOXES(HASH); \Link back
BOXES(HASH):= NOSYM;
NOSYM:= NOSYM +1;
end; \INSERT
proc GETCON; \Get a constant -- either by value or by name
int NEG;
begin
if ATOM = ^+ then RATOM;
if ATOM = ^- then [NEG:= true; RATOM] else NEG:= false;
case ATYPE of
INTCON: [if NEG then IATOM:= -IATOM; FACTYP:= INTEGER];
REALCON: [if NEG then RLATOM:= -RLATOM; FACTYP:= REAL];
IDENTIFIER:
begin
LOOKUP;
case IDTYPE of
INCON: [IATOM:= if NEG then -VAL else VAL;
FACTYP:= INTEGER];
RLCON: [RLATOM:= if NEG then -RLTBL(VAL) else RLTBL(VAL);
FACTYP:= REAL]
other ERROR(42);
end
other ERROR(42);
end; \GETCON
fproc BOOLEXP;
proc PROCAL;
int SVAL, SLEV, ARGCNT, SID, \K\;
begin
SVAL:= VAL; SLEV:= LEV; SID:= IDTYPE;
RATOM;
ARGCNT:= 0;
if ATOM = ^( then
begin
\ K:= 0; \\Offset to first integer argument
repeat begin
RATOM;
BOOLEXP;
if FACTYP = INTEGER then
[ARGCNT:= ARGCNT + INTSIZE;
\*** The following does not work if p-stack overflows ***
\ GENOP($2B40 + DSP-1); \\MOVE.L Dsp-1,K(A5)
\ GENOP(K);
\ DSP:= DSP-1;
\ K:= K +INTSIZE\]
else [ARGCNT:= ARGCNT + RLSIZE;
\ GENOP($F22D); \\FMOVE.D FPsp-1,K(A5)
\ GENOP($7400 + (FPSP-1) <<7);
\ GENOP(K);
\ FPSP:= FPSP-1;
\ K:= K +RLSIZE\];
end;
until ATOM#^,;
if ATOM#^) then ERROR(44) else RATOM;
end;
if ARGCNT>0 then GEN(\ARG\10, 0, ARGCNT, 2);
case of
SID>=INPROC & SID<=RLFPROC: \NORMAL PROCEDURE CALL
GEN(\CAL\5, SLEV+1, SVAL, 15);
SID=ININT ! SID=RLINT: \INTRINSIC PROCEDURE CALL
GEN(\CML\12, 0, SVAL, 2)
other GEN(\ECL\41, 0, SVAL, 3); \EXTERNAL PROCEDURE CALL
end; \PROCAL
proc BOOLEXP; \Boolean expression
\Outputs factor type (FACTYP)
int P1, P2, SFACTYP;
proc FACTOR;
func STRCON; \String constant function
int SPC;
begin
CLOSEOBJ;
SPC:= PC;
while CHAR # ^" do
begin \(GETCH -- optimized for speed)
case CHAR of
^^: [CHAR:= CHIN3; LIST(CHAR)];
EOF: [ERROR(63); exit]
other ;
HEX2OUT(CHAR); PC:= PC+1;
CHAR:= CHIN3;
LIST(CHAR);
end;
HEX2OUT(0); PC:= PC+1;
if PC & 1 then \Stay on even-byte boundary
[HEX2OUT(0); PC:= PC+1];
GETCH; \Skip the close quote
FACTYP:= INTEGER;
LASTOP:= -1; \Can't optimize
return SPC; \Return starting address of string
end; \STRCON
func ARRAYCON; \Constant arrays
int THISEL, NEXTEL, PNTR, SPC, I, INDIRECT, SFACTYP, FIRST;
def NULL= -1;
addr ENTRY, R;
begin
PNTR:= RESERVE(3 *INTSIZE);
THISEL:= PNTR;
THISEL(0):= NULL;
FIRST:= true; \(Used for mixed-mode detection)
repeat RATOM;
INDIRECT:= true;
case ATOM of
^[: ENTRY:= ARRAYCON;
^": ENTRY:= STRCON
other begin
INDIRECT:= false;
GETCON;
if FACTYP = INTEGER then ENTRY:= IATOM
else [ENTRY:= RESERVE(RLSIZE); \FACTYP = REAL
R:= addr RLATOM;
for I:= 0, RLSIZE-1 do ENTRY(I):= R(I)];
end;
NEXTEL:= RESERVE(3 *INTSIZE);
THISEL(1):= ENTRY;
THISEL(2):= INDIRECT;
THISEL(0):= NEXTEL;
NEXTEL(0):= NULL;
THISEL:= NEXTEL;
RATOM;
if FACTYP#SFACTYP & ~FIRST then \mixed mode\ ERROR(46);
SFACTYP:= FACTYP;
FIRST:= false;
until ATOM#^,;
if ATOM # ^] then ERROR(50);
SPC:= PC;
while PNTR(0) # NULL do \Follow list linkages & output data
begin
ENTRY:= PNTR(1);
if FACTYP=INTEGER then
begin
if PNTR(2) \indirect\ then [CLOSEOBJ; CHOUT3(^*)];
GENOP(SWAPWD(ENTRY));
GENOP(ENTRY);
end
else begin \(FACTYP = REAL)
CLOSEOBJ;
if PNTR(2) \INDIRECT\ then
[CHOUT3(^*);
HEX4OUT(SWAPWD(ENTRY));
HEX4OUT(ENTRY);
for I:= 1,RLSIZE-INTSIZE do HEX2OUT(0)]
\Fill out balance of entry
else for I:= 0, RLSIZE-1 do HEX2OUT(ENTRY(I));
PC:= PC +RLSIZE;
end;
PNTR:= PNTR(0);
end;
LASTOP:= -1; \Don't optimize
return SPC; \Return starting address of array
end; \ARRAYCON
proc SPECFAC; \Special character factor
int SVAL, SPC, R;
begin
case ATOM of
^(: [RATOM; \Parenthesized expression
BOOLEXP; \(Factor type is unchanged)
if ATOM#^) then ERROR(44)];
^": [GEN(\JMP\7, 0, 0, 7); \String constant
SPC:= PC -4;
SVAL:= STRCON;
FIXUP(SPC);
GEN(\IMM\11, 0, SVAL, 7)];
^[: [GEN(\JMP\7, 0, 0, 7);
SPC:= PC -4;
SVAL:= ARRAYCON;
FIXUP(SPC);
GEN(if FACTYP = INTEGER then \IMM\11 else \FIMM\$2C, 0, SVAL, 7)];
ADRSYM: begin \Get absolute heap address
RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
LOOKUP;
case IDTYPE of
INVAR, ADDRVAR, RLVAR:
if VAL < 0 then ERROR(43) else GEN(\ADR\33, LEV, VAL, 10);
\(Can't take address of a register variable)
UNDEF: ERROR(10) \(Undeclared name)
other ERROR(43); \(Variable expected)
FACTYP:= INTEGER;
end
other \illegal factor\ ERROR(26);
RATOM;
end; \SPECFAC
proc IDFAC; \Identifier factor
int SLEV, SVAL, SID;
begin
LOOKUP;
SID:= IDTYPE;
case IDTYPE of
UNDEF: ERROR(10);
INVAR, RLVAR: \Variable
begin
GEN(if SID = INVAR then \LOD\1 else \FLOD\$2A, LEV, VAL, 10);
RATOM;
if ATOM=^( then \It is indexed
begin
repeat [RATOM;
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
GEN(if SID = INVAR then \DBX\32
else \TRI\$3A, 0, 0, 0)]
until ATOM # ^, ;
if ATOM # ^) then ERROR(44) else RATOM;
end;
end;
ADDRVAR: begin \Address variable
RATOM;
if ATOM # ^( then GEN(\LOD\1, LEV, VAL, 10)
else begin \Array element reference
SLEV:= LEV; SVAL:= VAL;
RATOM;
BOOLEXP; \Index
if FACTYP # INTEGER then ERROR(47);
if ATOM # ^) then ERROR(44) else RATOM;
GEN(\LDX\2, SLEV, SVAL, 10);
end;
end;
INCON: begin \Integer constant identifier
if ABS(VAL)<=$7F & VAL#$80000000 then GEN(\IMS\36, 0, VAL, 2)
else GEN(\IMM\11, 0, VAL, 3);
RATOM;
end;
RLCON: begin \Real constant identifier
RLATOM:= RLTBL(VAL); GEN(\FIMM\$2C, 0, 0, 0);
RATOM;
end
other begin \Procedures used as functions (default)
PROCAL;
GEN(if SID&1 then \LOD\1 else \FLOD\$2A, 0, -1, 10);
\RETURN FUNCTION VALUES IN D0 (= -1)
end;
FACTYP:= if SID & 1 then INTEGER else REAL; \Odd IDs are integer
end; \IDFAC
begin \FACTOR
while ATOM = ^+ do RATOM; \Ignore unary "+"
if ATOM = ^- then \Unary "-"
[RATOM;
FACTOR;
GEN(if FACTYP = INTEGER then \NEG\17
else \FNEG\$31, 0, 0, 0)]
else case ATYPE of
SPECIAL: SPECFAC;
INTCON: [FACTYP:= INTEGER; \Integer constant
if ABS(IATOM)<=$7F & IATOM#$80000000 then
GEN(\IMS\36, 0, IATOM, 2)
else GEN(\IMM\11, 0, IATOM, 3);
RATOM];
REALCON: [FACTYP:= REAL; \Real constant
GEN(\FIMM\$2C, 0, 0, 0);
RATOM]
other IDFAC; \ATYPE = identifier (default)
end; \FACTOR
proc SHIFTEXP;
proc SHIFTX; int INOP;
[if FACTYP # INTEGER then \integer exptected\ ERROR(47);
RATOM; FACTOR;
if FACTYP # INTEGER then ERROR(47);
GEN(INOP, 0, 0, 0)];
begin \SHIFTEXP
FACTOR;
case ATOM of
LSLSYM: SHIFTX(\LSL\$3E); \ <<
LSRSYM: SHIFTX(\LSR\$3F) \ >>
other;
end; \SHIFTEXP
proc TERM;
int SFACTYP;
proc TERMX; int INOP, RLOP;
[RATOM; SHIFTEXP;
if SFACTYP # FACTYP then \mixed mode\ ERROR(46);
GEN(if FACTYP = INTEGER then INOP else RLOP, 0, 0, 0)];
begin \TERM
SHIFTEXP;
SFACTYP:= FACTYP;
loop case ATOM of
^*: TERMX(\MUL\15, \FMUL\$2F);
^/: TERMX(\DIV\16, \FDIV\$30)
other quit;
end; \TERM
proc ALGEXP; \Algebriac expression
int SFACTYP;
proc ALGX; int INOP, RLOP;
[RATOM; TERM;
if SFACTYP # FACTYP then \mixed mode\ ERROR(46);
GEN(if FACTYP = INTEGER then INOP else RLOP, 0, 0, 0)];
begin \ALGEXP
TERM;
SFACTYP:= FACTYP;
loop case ATOM of
^+: ALGX(\ADD\13, \FADD\$2D);
^-: ALGX(\SUB\14, \FSUB\$2E)
other quit;
end; \ALGEXP
proc LOGEXP; \Logical expression
int SFACTYP;
proc LOGX; int INOP, RLOP;
[RATOM; ALGEXP;
if SFACTYP # FACTYP then \mixed mode\ ERROR(46);
GEN(if FACTYP = INTEGER then INOP else RLOP, 0, 0, 0);
FACTYP:= INTEGER];
begin \LOGEXP
if ATOM=NOTSYM ! ATOM=^~ then \Unary 'NOT' operator
[RATOM; LOGEXP;
if FACTYP # INTEGER then ERROR(47);
GEN(\NOT\28, 0, 0, 0)]
else [ALGEXP;
SFACTYP:= FACTYP;
case ATOM of
^=: LOGX(\EQ\18, \FEQ\$32);
^#: LOGX(\NE\19, \FNE\$33);
^>: LOGX(\GT\21, \FGT\$35);
^<: LOGX(\LT\23, \FLT\$37);
GESYM: LOGX(\GE\20, \FGE\$34);
LESYM: LOGX(\LE\22, \FLE\$36)
other];
end; \LOGEXP
proc BOOLTERM; \Boolean "&" expressions
begin
LOGEXP;
loop [if ATOM=^& then
[if FACTYP # INTEGER then ERROR(47);
RATOM; LOGEXP; GEN(\AND\27, 0, 0, 0);
if FACTYP # INTEGER then ERROR(47)]
else quit];
end; \BOOLTERM
proc BEXPX; int INOP;
begin
if FACTYP # INTEGER then \integer exptected\ ERROR(47);
RATOM; BOOLTERM;
if FACTYP # INTEGER then ERROR(47);
GEN(INOP, 0, 0, 0);
end; \BEXPX
begin \BOOLEXP
if ATOM=IFSYM then \'IF' expression
[RATOM; BOOLEXP;
GEN(\JOC\8, 0, 0, 7); P1:= PC -4;
if ATOM # THENYM then ERROR(22);
RATOM; BOOLEXP; SFACTYP:= FACTYP;
GEN(\JMP\7, 0, 0, 7); P2:= PC -4;
if FACTYP = INTEGER then DSP:= DSP-1 else FPSP:= FPSP-1;
FIXUP(P1);
if ATOM # ELSEYM then ERROR(30);
RATOM; BOOLEXP;
if SFACTYP # FACTYP then \mixed mode\ ERROR(46);
FIXUP(P2)]
else begin \Boolean "!" (or) expressions
BOOLTERM;
loop case ATOM of
^!: BEXPX(\OR\$1A);
^|: BEXPX(\EOR\$1D)
other quit;
end;
end; \BOOLEXP
\----------------------------------------------------------------------
proc SSTATEMENT(SSTK); \(For 'QUIT's in 'CASE' statments)
int SSTK;
proc STATEMENT;
int P2, P3, SFIXS, SLEV, SVAL, SFACTYP, I, SDSP;
proc ASSIGN; \Assignment statement
\ (Also includes procedure calls)
proc ASSX;
[if ATOM # GETSYM then ERROR(21);
RATOM;
BOOLEXP]; \Right-hand side of assignment
begin \ASSIGN
if ATYPE # IDENTIFIER then
\Bad start of a statement\ [ERROR(20); SKIPIT; return];
LOOKUP; if IDTYPE = UNDEF then [ERROR(10); SKIPIT; return];
SLEV:= LEV; SVAL:= VAL; \(BOOLEXP may change LEV & VAL)
if IDTYPE>=INPROC & IDTYPE<=RLEXT then PROCAL
else if IDTYPE=INVAR ! IDTYPE=RLVAR then
begin
SFACTYP:= if IDTYPE = INVAR then INTEGER else REAL;
RATOM;
if ATOM=^( then \Indexed
begin
GEN(if SFACTYP = INTEGER then \LOD\1 \+++
else \FLOD\$2A, SLEV, SVAL, 10);
RATOM;
BOOLEXP; \First index
if FACTYP # INTEGER then ERROR(47);
while ATOM = ^, do \Multiple indexing
[GEN(if SFACTYP = INTEGER then \DBX\32
else \TRX\$39, 0, 0, 0);
RATOM;
BOOLEXP;
if FACTYP # INTEGER then ERROR(47)];
GEN(if SFACTYP = INTEGER then \DBA\30
else \TRA\$38, 0, 0, 0);
if ATOM#^) then ERROR(44) else RATOM;
ASSX; \TOS now points to array element
GEN(if SFACTYP = INTEGER then \STD\31
else \STT\$3B, 0, 0, 0);
end
else [ASSX;
GEN(if SFACTYP = INTEGER then \STO\3
else \FSTO\$2B, SLEV, SVAL, 10)];
if FACTYP # SFACTYP then \mixed mode\ ERROR(46);
end
else if IDTYPE = ADDRVAR then \Address variable
begin
RATOM;
if ATOM = ^( then \Indexed
[RATOM;
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
if ATOM # ^) then ERROR(44) else RATOM;
ASSX;
GEN(\STX\4, SLEV, SVAL, 10)]
else [ASSX;
GEN(\STO\3, SLEV, SVAL, 10)];
if FACTYP # INTEGER then \mixed mode\ ERROR(46);
end
else \statement starting with a constant\ [ERROR(27); SKIPIT];
end; \ASSIGN
proc CASER; int TYPE;
int SPC1, SPC2, SPC3;
proc CASER2; \Compile expression(s) and statement
begin
RATOM; \Expression
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
GEN(TYPE, 0, 0, 7); \Conditional jump of some type
SPC1:= PC -4; \SPC1 will be fixed to point to next line
if ATOM=^, then \Multiple expressions
[SPC3:= SPC1;
repeat RATOM;
GENPC(-4); \Reverse the status of the conditional jump
GENOP(OBJBUF(OBJFILL) | $0100); \(Beware of optimized JOC's)
GENOP(SPC3-PC); \Branch offset (branches must be long)
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
GEN(TYPE, 0, 0, 7);
SPC1:= PC -4; \Save address of last conditional jump
until ATOM # ^,;
FIXUP(SPC3)]; \So as to jump to statement
if ATOM # ^: then [ERROR(49); SKIPIT; return];
RATOM;
STATEMENT;
end; \CASER2
begin \CASER
CASER2;
GEN(\JMP\7, 0, 0, 7); \Jump out of case statement
SPC2:= PC -4;
FIXUP(SPC1); \Fix conditional jump to go to expression
while ATOM = ^; do \ on the next line
[CASER2;
if LASTOP#\JMP\7 then GEN(\JMP\7, 0, SPC2, 7); \2-jump exit
FIXUP(SPC1)]; \Jump to expr on next line
if ATOM # OTHSYM then ERROR(29);
RATOM;
STATEMENT; \'OTHER'
FIXUP(SPC2);
end; \CASER
begin \STATEMENT
case ATOM of
BEGSYM, ^[:
begin
RATOM;
loop begin
if ATOM = ELSEYM then [ERROR(52); RATOM];
if ATOM = OTHSYM then [ERROR(53); RATOM];
STATEMENT;
case ATOM of
^;: RATOM;
ENDSYM: quit;
^]: quit;
EOF: [ERROR(62); exit]
other \semi expected\ ERROR(41);
end;
RATOM; \Read past the 'END'
end;
CASEYM: begin \Case statement
RATOM;
if ATOM = OFSYM then CASER(\JOC\8)
else begin
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
if ATOM # OFSYM then [ERROR(48); SKIPIT; return];
STKLOD:= STKLOD+1;
CASER(\CAJ\37);
GEN(\DRP\40, 0, 0, 0);
STKLOD:= STKLOD-1;
end;
end;
QUITYM: begin \Quit statement
SDSP:= DSP; \Don't interfere with the stack that GEN keeps
for I:= SSTK, STKLOD-1 do GEN(\DRP\40, 0, 0, 0);
if FIXCNT > QUITMAX then [ERROR(4); FIXCNT:= QUITMAX -1];
GEN(\JMP\7, 0, 0, 7); \This will be "FIXED UP" at end of 'LOOP'
FIXES(FIXCNT):= PC -4;
FIXCNT:= FIXCNT +1;
DSP:= SDSP;
RATOM;
end;
IFSYM: begin \If statement
RATOM;
BOOLEXP;
GEN(\JOC\8, 0, 0, 7);
P3:= PC-4;
if ATOM # THENYM then [ERROR(22); SKIPIT; return];
RATOM;
STATEMENT;
if ATOM = ELSEYM then
[GEN(\JMP\7, 0, 0, 7);
P2:= PC -4;
FIXUP(P3);
P3:= P2;
RATOM;
STATEMENT];
FIXUP(P3);
end;
REPSYM: [GEN(\BAL\$27, 0, 0, 0); \Make sure the stack is balanced
P2:= PC; \Repeat statement
repeat RATOM; STATEMENT until ATOM#^;;
if ATOM # UNTSYM then [ERROR(28); SKIPIT; return];
RATOM;
BOOLEXP;
GEN(\JOC\8, 0, P2, 7)];
WHILYM: [RATOM; \While statement
GEN(\BAL\$27, 0, 0, 0); \Make sure the stack is balanced
P2:= PC;
BOOLEXP;
GEN(\JOC\8, 0, 0, 7);
P3:= PC-4;
if ATOM # DOSYM then [ERROR(23); SKIPIT; return];
RATOM;
STATEMENT;
GEN(\JMP\7, 0, P2, 7);
FIXUP(P3)];
RETSYM: begin \Return statement
RATOM;
if ATOM#ELSEYM & ATOM#OTHSYM & ATOM#^; & ATOM#^] &
ATOM#ENDSYM & ATOM#UNTSYM then \Store the returned
[BOOLEXP; \ value in "global 0"
GEN(if FACTYP=INTEGER then\STO\3 else \FSTO\$2B, 0, -1, 10)];
\Return function values in D0 (= -1)
SDSP:= DSP;
\Don't interfere with GEN which keeps track of the p-stack pointer
for I:= 0, STKLOD-1 do GEN(\DRP\40, 0, 0, 0);
GEN(\RET\$06, LEVEL, 0, 0);
DSP:= SDSP;
end;
LOOPYM: begin \Loop statement
SFIXS:= FIXCNT;
RATOM;
GEN(\BAL\$27, 0, 0, 0); \Make sure the stack is balanced
P2:= PC;
SSTATEMENT(STKLOD);
if LASTOP#\JMP\7 then GEN(\JMP\7, 0, P2, 7);
while FIXCNT>SFIXS do \"FIX UP" the jumps for the 'QUIT'S
[FIXCNT:= FIXCNT-1; FIXUP(FIXES(FIXCNT))];
end;
FORSYM: begin \For statement
RATOM;
if ATYPE # IDENTIFIER then [ERROR(33); SKIPIT; return];
LOOKUP;
if IDTYPE = UNDEF then ERROR(10)
else if IDTYPE # INVAR then ERROR(33);
SLEV:= LEV; SVAL:= VAL;
RATOM;
if ATOM # GETSYM then [ERROR(21); SKIPIT; return];
RATOM;
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
GEN(\STO\3, SLEV, SVAL, 10);
if ATOM # ^, then [ERROR(24); SKIPIT; return];
RATOM;
BOOLEXP;
if FACTYP # INTEGER then ERROR(47);
if ATOM # DOSYM then [ERROR(23); SKIPIT; return];
GEN(\JMP\7, 0, 0, 7);
P2:= PC -4;
GEN(\BAL\$27, 0, 0, 0); \Make sure the stack is balanced
P3:= PC;
RATOM;
STKLOD:= STKLOD +1;
STATEMENT;
STKLOD:= STKLOD -1;
GEN(\INC\25, SLEV, SVAL, 10);
FIXUP(P2);
GEN(\LOD\1, SLEV, SVAL, 10);
GEN(\FOR\24, 0, P3, 7);
end;
EXITYM: begin
RATOM;
GEN(\EXIT\0, LEVEL, 0, 0); \'EXIT' statement
end;
ELSEYM: []; \Null statement
OTHSYM: [];
^;: [];
^]: [];
ENDSYM: [];
UNTSYM: [];
EOF: [] \(This is mostly an academic point)
other ASSIGN;
end; \STATEMENT
begin \SSTATEMENT
\Trick to adjust stack (with DRP's) when a 'QUIT' is in a 'CASE' statement.
STATEMENT;
end;
\----------------------------------------------------------------------
proc PROCEDURE;
int P1, DX, \Heap space requirement counter
I,
SNOSYM,
FPBASE, \PC at end of declarations
FPROCNT; \Count of pending forward procedures
proc CODDEC; \Declare intrinsic names
int SID;
begin
SID:= ININT; \Default is integer intrinsic
RATOM;
if ATOM = REALYM then [SID:= RLINT; RATOM]
else if ATOM = INTSYM then RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
[RATOM;
if ATOM # ^= then ERROR(40);
RATOM;
GETCON; if FACTYP # INTEGER then ERROR(47);
if IATOM<0 ! IATOM>127 then ERROR(7);
INSERT(SID, LEVEL, IATOM);
RATOM;
if ATOM = ^, then RATOM];
if ATOM # ^; then ERROR(41) else RATOM;
end; \CODDEC
proc CONDEC; \Declare constant names
int CNTR, SSNO;
begin
RATOM;
CNTR:= 0;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
begin
RATOM;
if ATOM # ^= then [INSERT(INCON, LEVEL, CNTR); CNTR:= CNTR+1]
else begin
SSNO:= NOSYM;
INSERT(INCON, LEVEL, NORLSY); \Insert ID now
RATOM; \ fix up parms later
GETCON;
if FACTYP = INTEGER then SYMVAL(SSNO):= IATOM
else \FACTYP = REAL
[SYMTYP(SSNO):= RLCON;
if NORLSY >= RLMAX then
[ERROR(2); NORLSY:= RLMAX-1];
RLTBL(NORLSY):= RLATOM;
NORLSY:= NORLSY +1];
RATOM;
end;
if ATOM = ^, then RATOM;
end;
if ATOM # ^; then ERROR(41) else RATOM;
end; \CONDEC
proc VARDEC(TYPE); \Declare variables: 'INT', 'REAL' & 'ADDR'
int TYPE;
begin
RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
[INSERT(TYPE, LEVEL, DX);
DX:= DX + (if TYPE=RLVAR then RLSIZE else INTSIZE);
RATOM;
if ATOM = ^, then RATOM];
if ATOM # ^; then ERROR(41) else RATOM;
end; \VARDEC
proc RVARDEC; \Declare register variables: INT, REAL & ADDR
int I, SNOSYM; \E.G: 'REG' 'INT' FROG, AARDVARK, PIG;
begin
SNOSYM:= NOSYM;
RATOM;
case ATOM of
INTSYM: VARDEC(INVAR);
ADRSYM: VARDEC(ADDRVAR);
REALYM: VARDEC(RLVAR)
other ERROR(55); \Variable declaration expected
\Fix symbol table entries to indicate register variables. This gyration
\ is required because of argument passing. Each register variable has a
\ corresponding normal memory variable which might be passed an argument.
\ The level entry in the symbol table (SYMLEV) is meaningless for register
\ variables, so it is used to hold the offset of the corresponding memory
\ variable.
for I:= SNOSYM, NOSYM-1 do
begin
SYMLEV(I):= SYMVAL(I); \Get offset of memory variable
SYMVAL(I):= -PSTOP; \Get register from top of p-stack
PSTOP:= PSTOP -1; \ -8 is D7, etc.
if PSTOP < 3 then ERROR(8); \Too many register variables
end;
end; \RVARDEC
proc EXTDEC; \Declare external procedures
int SID;
begin
SID:= INEXT; \Default is integer external procedure
RATOM;
if ATOM = REALYM then [SID:= RLEXT; RATOM]
else if ATOM = INTSYM then RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
[RATOM;
if ATOM # ^= then ERROR(40);
RATOM;
GETCON; if FACTYP # INTEGER then ERROR(47);
INSERT(SID, LEVEL, IATOM);
RATOM;
if ATOM = ^, then RATOM];
if ATOM # ^; then ERROR(41) else RATOM;
end; \EXTDEC
proc FPRDEC; \Declare forward referenced procedures
int SID;
begin
SID:= INFPROC; \Default is integer forward procedure
RATOM;
if ATOM = REALYM then [SID:= RLFPROC; RATOM]
else if ATOM = INTSYM then RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
[RATOM;
GEN(\JMP\7, 0, PC, 7); \Jump to self ("FIXED UP" later)
INSERT(SID, LEVEL, PC-4);
FPROCNT:= FPROCNT+1;
if ATOM = ^, then RATOM];
if ATOM # ^; then ERROR(41) else RATOM;
end; \FPRDEC
proc EPRDEC; \Declare external procedures
int SID;
begin
SID:= INEPROC; \Default is integer external procedure
RATOM;
if ATOM = REALYM then [SID:= RLEPROC; RATOM]
else if ATOM = INTSYM then RATOM;
if ATYPE # IDENTIFIER then ERROR(45);
while ATYPE = IDENTIFIER do
[RATOM;
GEN(\JMP\7, 0, PC, 64); \Jump to self "FIXED UP" by 'LINK' (%)
INSERT(SID, LEVEL, PC-6);
if ATOM = ^, then RATOM];
if ATOM # ^; then ERROR(41) else RATOM;
end; \EPRDEC
proc PROCDEC; \Declare procedure names
int SNOSYM, HASH, I, K, SID, SNORL, SPSTOP, SPSTOP0;
begin
SID:= INPROC; \Typed procedure (for functions)
RATOM;
if ATOM = REALYM then [SID:= RLPROC; RATOM]
else if ATOM = INTSYM then RATOM;
if ATYPE # IDENTIFIER then ERROR(45) else LOOKUP;
if IDTYPE=INFPROC ! IDTYPE=RLFPROC then
\Procedure has been previously declared by a 'FPROC' or 'FFUNCT'
[if LEVEL#LEV then ERROR(65);
FIXUP(VAL); \('FPROC' & 'PROC' must be same scope)
SYMVAL(SYMNUM):= PC;
SYMTYP(SYMNUM):= if IDTYPE = INFPROC then INPROC else RLPROC;
\(Speed up things a little by changing forward proc to normal proc)
if SID # SYMTYP(SYMNUM) then \mixed mode\ ERROR(46);
if VAL >= FPBASE then FPROCNT:= FPROCNT-1]
else if IDTYPE=INEPROC ! IDTYPE=RLEPROC then
\Procedure has been previously declared by an 'EPROC' or 'EFUNCT'
[if LEVEL # LEV then ERROR(65); \('EPROC' & 'PROC' must be same scope)
SYMVAL(SYMNUM):= PC;
SYMTYP(SYMNUM):= if IDTYPE = INEPROC then INPROC else RLPROC;
\(Speed up things a little by changing external proc to normal proc.
\ This also prevents flagging a multiple-definition error)
if SID # SYMTYP(SYMNUM) then \mixed mode\ ERROR(46)]
else INSERT(SID, LEVEL, PC);
LEVEL:= LEVEL+1; if LEVEL>4 then ERROR(5);
\Eat the argument list as a comment
while CHAR#^; & CHAR#\CR\$0D do GETCH; \Special comment stops on CR
if CHAR # ^; then ERROR(41);
GETCH; RATOM;
SNOSYM:= NOSYM; SNORL:= NORLSY; SPSTOP:= PSTOP; SPSTOP0:= PSTOP0;
PROCEDURE;
if ATOM # ^; then ERROR(41) else RATOM;
while NOSYM > SNOSYM do \Restore symbol table to previous level
\I.e. remove the identifiers which were local to this procedure
[NOSYM:= NOSYM-1;
HASH:= 0; K:= NOSYM;
for I:= 0, SIGCHAR-1 do
[HASH:= HASH +SYMBOL(K); K:= K +SYMAX];
BOXES(HASH &$FF):= SYMPNT(NOSYM)];
NORLSY:= SNORL;
PSTOP:= SPSTOP;
PSTOP0:= SPSTOP0;
LEVEL:= LEVEL-1;
end; \PROCDEC
begin \PROCEDURE
DX:= 0;
SNOSYM:= NOSYM;
DSP:= 1; \Init pseudo stack pointer
FPSP:= 1; \Init floating point p-stack pointer
PSTOP0:= PSTOP; \(For generating HPI and RET)
GEN(\JMP\7, 0, 0, 7);
P1:= PC -4;
loop case ATOM of
REGSYM: RVARDEC;
INTSYM: VARDEC(INVAR);
ADRSYM: VARDEC(ADDRVAR);
REALYM: VARDEC(RLVAR);
CODSYM: CODDEC;
EXTNYM: EXTDEC;
DEFSYM: CONDEC;
EPRSYM, EFUNYM:
if LEVEL = 0 then EPRDEC else [ERROR(68); SKIPIT]
other quit;
FPROCNT:= 0;
FPBASE:= PC;
loop case ATOM of
LNKSYM:
[if LEVEL # 0 then ERROR(68);
CLOSEOBJ;
CHOUT3(^%);
RATOM;
case ATOM of PROCYM, FUNSYM: PROCDEC
other ERROR(67)];
PROCYM, FUNSYM:
PROCDEC;
FPRSYM, FFUNYM:
FPRDEC \'FPROC' cannot precede a 'DEF'
other quit;
\If there is nothing to jump over then eliminate the jump
if PC = P1+4 then GENPC(-4) else FIXUP(P1);
GEN(\HPI\9, LEVEL, DX, 2); \Reserve space for local variables
if DX >= $8000 then \too many variables\ ERROR(1);
\Generate code to initialize any register variables in case they are used
\ to receive arguments:
for I:= SNOSYM, NOSYM-1 do
if SYMVAL(I) < 0 then \It might be a register variable
case SYMTYP(I) of INVAR, ADDRVAR:
[GEN(\LOD\1, LEVEL, SYMLEV(I), 10); \SYMLEV holds offset to memory
GEN(\STO\3, 0, SYMVAL(I), 10)]; \SYMVAL is a register (-8 = D7)
RLVAR: [] \$$$
other;
SSTATEMENT(STKLOD); \(STKLOD will always be zero here)
GEN(\RET\6, LEVEL, 0, 0);
if FIXCNT # 0 then \some 'QUIT's not in a 'LOOP'\ ERROR(60);
if FPROCNT # 0 then \unresolved fwd references\ ERROR(66);
end; \PROCEDURE
\======================================================================
begin \MAIN: Display title and initialize
IDENT:= RESERVE(SIGCHAR);
FIXES:= RESERVE(QUITMAX *INTSIZE);
SYMBOL:= RESERVE(SIGCHAR *SYMAX); \Symbol table
SYMTYP:= RESERVE(SYMAX);
SYMVAL:= RESERVE(SYMAX *INTSIZE);
SYMLEV:= RESERVE(SYMAX);
SYMPNT:= RESERVE(SYMAX *INTSIZE);
BOXES:= RESERVE(256 *INTSIZE); \Hash table
\RLTBL:= RLRES(RLMAX); *** DEBUG *** $$$
HEXDIGIT:= "0123456789ABCDEF";
ERRBUF:= RESERVE($100);
OBJBUF:= RESERVE(8 *INTSIZE);
DEFAULT:= [7]; \Default listing device no.
TEXT(TV, "-- XPL0 COMPILER, VER 5.7-68Kx15 --
");
loop begin
TEXT(TV, "CHANGE DEFAULTS (N/Y)? ");
if (CHIN(KB)!$20) # ^y then quit;
TEXT(TV, "LISTING DEVICE NUMBER? ");
DEFAULT(0):= INTIN(KB);
OPENI(KB);
end;
LSTDEV:= DEFAULT(0);
OPENO(LSTDEV);
OPENI(3); OPENO(3);
TEXT(TV, "COMPILING...
");
PC:= 0;
CHOUT3(^;); HEX4OUT(PC);
OPENOBJ;
ERRPTR:= 0; \Initialize some stuff
for II:= 0, $FF do ERRBUF(II):= 0;
\CASEIN:= false;
LEVEL:= 0; PSTOP:= 8; FPSTOP:= 4;
LASTOP:= -1; STKLOD:= 0; NOSYM:= 0; NORLSY:= 0; FIXCNT:= 0;
for II:= 0, 255 do BOXES(II):= \empty\ -1; \Empty the symbol table
ERRCNT:= 0;
GETCH; RATOM;
PROCEDURE; \Compile main procedure (the program)
while ATOM = ^; do RATOM;
if ATOM # EOF then \more code after end\ [ERROR(61); PROCEDURE];
if LSTDEV = TV then CRLF(TV);
TEXT(TV, "LENGTH (BYTES): "); INTOUT(TV, PC); TEXT(TV, "
ERRORS DETECTED: "); INTOUT(TV, ERRCNT); CRLF(TV);
CLOSEOBJ;
if ERRCNT = 0 then CLOSE(3);
CLOSE(LSTDEV);
end; \MAIN
V, "
ERRORS DETECTED: "); INTOUT(TV, ERRCNT); CRLF(TV);
CL